The R Ecosystem

This course will use R for model exploration, data analysis, and writing results. If you do not have R on your laptop, please download it from here and install the appropriate version for your platform.

R Version

I am currently using R version 4.3.3 for this document.

To check the version of your R instance, type:

version

If the major version of your install is 4, you should be fine. If it is earlier, you should upgrade. If the first number in the minor version is lower than this, you should also consider upgrading. The minor “dot version” is not as important.

Upgrading from a Previous Version

R stores all the libraries in a version-specific folder structure. Before you upgrade, you may want to make a backup of which sets of libraries/packages you have installed already so that when you upgrade, you can automatically reinstall the packages into the new system. Here is how you’d do that.

  1. Make a record of the packages you already have installed. This takes the list of packages you have, pulls out the names of each package and saves it as an R data file (.rda) on your Desktop. After this, you can close R.
pkgs <- installed.packages()
pkgs <- names( is.na(pkgs[,4]))
save(pkgs,file='~/Desktop/pkgs.rda')
  1. Download and Install the new version of R.

If you are on Windows… I’m sorry, I feel for you. Make sure you install the UCRT package that is in the base install documents. There may be other issues that come up, I am not familiar with this platform but in almost all the workshops I lead, there are Windows issues.

  1. Find the packages you need to install by comparing it to what you had previously and install them all at once.
new_pkgs <- installed.packages()
new_pkgs <- names( is.na(new_pkgs[,4]))
load("~/Desktop/pkgs.rda")
to_install <- setdiff( pkgs, new_pkgs )
install.packages( to_install )
update.packages( ask=FALSE )

Updating your current set of packages.

If you just updated R, you can skip this section. If you have not updated your package list in a while, you may want to update any out-of-date packages you may have on your system. Do this when you are not needing to use R for a while, depending upon how much you need to upgrade, it may take a while.

update.packages( ask=FALSE )

If you do not put the ask=FALSE part in there, you’ll be tethered to the computer because it will ask your permission to install each package one at a time…

RStudio

I am assuming that you are using RStudio for this course. If you prefer to use another platform such as Jupyter Notebooks, Visual Studio, or other IDE’s, you are on your own. However, this course will require you to produce markdown documents in HTML or DOCX format.

If you do not have the most recent version of RStudio, please download it from here

Quarto

We are going to use Quarto in this course to make integrate our data analysis, graphical output, report writing. If you have not used quarto before, you will need to download the most recent version. There is a complete discussion of how to start with Quarto on the website. Download the version for your compute platform and choose RStudio (or whatever IDE) you are using. It will set you up.

RStudio Projects

RStudio has the concept of a project for code and data organization. I highly recommend that you use a single project for this course. This will co-locate all the data, all the code, and all the markdown that you will be given and that you create for this class. It will also seemlessly handle working directories and all the other crap that befuddles many students. To make a new package, open up RStudio and do the following:

  1. From the File menu, select New Projct

  2. Select New Directory from the popup dialog. This will create a new folder to put all your materials into.

  1. Select New Project for this, we are going to create a generic project to put all the course materials into.

  1. Create a name for the folder, perhaps select the name of the class or some other designation that works with your file organization system. Also, put it somewhere that will be useable (I had a student recently who only put stuff in the Downloads folder for some reason…).

  1. Select Create Project.

Now, whenever you get a document or data file, put it in this folder.

It is important for me to emphasize this. When you download a data set or markdown document or R script and simply double click on it, it will most likely be in your Downloads folder.

Move it to the project folder so it is co-located with all the other stuff for this course. If you do not, some of the code may not work properly and you’ll be hating yourself. (Dyer’s #1 rule, do things now so ‘future self’ does not hate decisions that ‘past self’ made…)

More importantly, when RStudio opens, it will default to opening the most recent project (which in all likelihood will be this project).

Markdown

In R, we have two categories of documents that we will be using.

R Scripts

You can write pure R code in scripts, which are files that are executed one line at a time, identical to you typing it into the R console. These files are saved with the suffix .R. These are great for pure data manipulation and coding.

To create an R Script select File\(\to\)New File\(\to\)R Script.

To execute R code in a script, we can select Source or Run from the button at top of the RStudio editor window (just above the script itself). Or, you can execute the code in a script from another location (e.g., from another script) using the function source() and passing it the path (relative to the document you are using right now) to the script. It will then grab the code from the script and execute it as if it was part of this script.

source("path_to/my_cool_script.R")

This is very helpful, particularly if you routinely get raw data that needs some preliminary QA/QC, you can set up a script that will clean it up and call it as necessary.

It is important to think about compartmenatalization of your code. If you ever find yourself typing or (worse yet) copy-and-pasting code you’ve used before into a new analysis—STOP. One of our goals is to write as few lines of code as possible. Errors (logical and grammatical) increase linearly with the number of lines of code you write. The Dont Repeat Yourself (DRY) principle is key to success in data analytics. Refactor things for reuse will always be benefitical to you in the long run.

One last thing. When you write code in a script, use a lot of comments to explain what you are doing in the code. Here is the syntax of a single line comment.

# This is a comment and will not be interpreted by R

‘Future self’ will thank you when you come back to this file and try to figure out how to use it again.

Quarto Documents

The next kind of file we will be working with are quarto markdown documents. These documents let you mix together the R code (like the scripts), graphical output, tabular output, and the larger narratives around data and analyses you are creating. It is also possible to link the numerical values assocaited with an analysis to in-text values so that if the data change the text changes as well. Figure and table numbering and citations are also updated automatically. It is in your best interest (if your time is important to you) to become proficient in how markdown and R interact.

The document you are reading right here is a quarto document. These files end in the file suffix .qmd.

If you have not used markdown before, you need to jump in right now. Here are some resources for you from a Data Literacy course I teach which will help you get up-to-speed on using Markdown in RStudio.

  • Slides describing the rationale for Markdown and how to use it in RStudio where you can mix textual content with data, code, and output.
  • A longer narrative on the topic that goes into more depth than the slides that includes code and example output.
  • A Video on Markdown and Notebooks in RStudio that I made during the COVID lockdown when teaching this course.
  • External resources realted to using markdown:

Data Maniuplation

If we look at the amount of time we allocate to our data analytics, the vast majority of it is spent on working on the data, manipulating it, getting it into a format that is usable, and a very small fraction of it is spent on running an actual analysis. Usually, by the time we get to the analysis, we are doing a simple function call with our formatted data.

analysis( myData )

I am hoping that you are versed in using tidyverse for data manipulation. This is a constellation of packages that help you take raw data (genotypes, rasters, shapefiles, csv files, etc.) and perform operations on it. Loading in tidyverse is done by calling the library directly.

library( tidyverse )

If you are getting a “library not found” for this, install it using the following.

install.packages( "tidyverse" )

Here is an example of how it can be used to summarize data from the built-in Iris data set.

summary( iris )

The following code does the following:
- outputs the raw data. - creates a new column of data with genus and species. - groups by species. - summarizes by taking the mean values for petal length and width. - saves as a data.frame to a local variable.

iris |>
  mutate( Plant = paste( "Iris",Species)) |>
  group_by( Plant ) |>
  summarize( Length = mean( Petal.Length ), 
             Width = mean( Petal.Width ) ) -> iris_summary

And this is what the output looks like.

iris_summary 

If you were unsure what the output of the code was going to look like or do not understand what was done, you may need to work a bit on getting up to speed on tidyverse.

Here are some resources on using tidyverse from a few different sources.

Graphics

Part of the tidyverse is the ggplot2 library. This is the primary and dominant graphing output for R and will be used throughout this course. You should be familiar with the code sets below (enough to understand what they are doing) and be able to produce basic data graphics.

A basic scatter plot with groupings and a trend line (@fig-scatter).

iris |> 
  ggplot( aes(Petal.Width, Petal.Length ) ) + 
  stat_smooth( formula = y~x,
               method="lm",
               se=FALSE, col="gray", 
               lwd=0.5, lty=2 ) + 
  geom_point( aes( color = Species ) ) +
  xlab("Petal Width (mm)") +
  ylab("Petal Length (mm)")

A categorical data output using boxplot() (@fig-boxplot) or violin() (@fig-violin) display to compare continuous responses from factors.

iris |> 
  ggplot( aes(Species,Petal.Length) ) + 
  geom_boxplot(notch=TRUE) +  
  xlab("Iris species") +
  ylab("Petal Length (mm)")
iris |> 
  ggplot( aes(Species,Petal.Length) ) + 
  geom_violin() + 
  geom_jitter( width = 0.05, alpha = 0.5) +
  xlab("Iris species") +
  ylab("Petal Length (mm)")

A representation of the distribution of variables (@fig-density) across different groups.

iris |>
  ggplot( aes(Sepal.Length, fill=Species) ) + 
  geom_density( alpha = 0.75, col="darkgray") +
  xlab("Iris species") +
  ylab("Petal Length (mm)") + 
  xlim( c(3,9) )

If these methods are new to you, here are some resources that will help.

Tabular Output

The last part of this review will focus on tabular output. Many times, we have data we need to summarize (see above the tidyverse example data that produced a data.frame of mean petal sizes) and would like to create a table in our document that will present the results to the reader (or an ANOVA table or whatever). The most common approach in R is to use the following:

library( knitr )
library( kableExtra )

Here is the data from above:

iris_summary

We can make this into a table for publication output as is (@tbl-iris).

iris_summary |>
  kable() |>
  kable_styling( bootstrap_options = c("striped", "hover", "condensed", "responsive") ) |>
  add_header_above(c(" " = 1, "Mean Petal Measurement (mm)"=2) ) |>
  footnote(general="N = 50 per species.", footnote_as_chunk = TRUE)
 

There is a great webpage for kableExtra that describes a lot of additional functionality for this library here. I recommend you take a look at it.

Conclusion

So this should be sufficient for what we need in this course. If you have any concerns about your ability to do any of the above, please let me know and we can get to work on helping you get to where you need to be.

If you have any questions, feel free to drop by my office (LFSC 105c), jump onto my Office Hours Zoom (linked form Canvas), or send me an email.

LS0tCnRpdGxlOiAiUiBFY29zeXN0ZW0gUmV2aWV3IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiFbXShtZWRpYS9HZW5lcmF0ZWRJbWFnZS5wbmcpCgojIFRoZSBgUmAgRWNvc3lzdGVtCgpUaGlzIGNvdXJzZSB3aWxsIHVzZSBgUmAgZm9yIG1vZGVsIGV4cGxvcmF0aW9uLCBkYXRhIGFuYWx5c2lzLCBhbmQgd3JpdGluZyByZXN1bHRzLiAgSWYgeW91IGRvIG5vdCBoYXZlIGBSYCBvbiB5b3VyIGxhcHRvcCwgcGxlYXNlIGRvd25sb2FkIGl0IGZyb20gW2hlcmVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnKSBhbmQgaW5zdGFsbCB0aGUgYXBwcm9wcmlhdGUgdmVyc2lvbiBmb3IgeW91ciBwbGF0Zm9ybS4KCgojIyMgUiBWZXJzaW9uCgo6Ojp7LmNhbGxvdXQtaW5mb30KSSBhbSBjdXJyZW50bHkgdXNpbmcgUiB2ZXJzaW9uIGByIHBhc3RlKCB2ZXJzaW9uJG1ham9yLCB2ZXJzaW9uJG1pbm9yLCBzZXA9Ii4iKWAgZm9yIHRoaXMgZG9jdW1lbnQuICAKOjo6CgpUbyBjaGVjayB0aGUgdmVyc2lvbiBvZiB5b3VyIGBSYCBpbnN0YW5jZSwgdHlwZToKCmBgYHtyfQp2ZXJzaW9uCmBgYAoKSWYgdGhlIG1ham9yIHZlcnNpb24gb2YgeW91ciBpbnN0YWxsIGlzIGByIHZlcnNpb24kbWFqb3JgLCB5b3Ugc2hvdWxkIGJlIGZpbmUuICBJZiBpdCBpcyBlYXJsaWVyLCB5b3Ugc2hvdWxkIHVwZ3JhZGUuICBJZiB0aGUgZmlyc3QgbnVtYmVyIGluIHRoZSBtaW5vciB2ZXJzaW9uIGlzIGxvd2VyIHRoYW4gdGhpcywgeW91IHNob3VsZCBhbHNvIGNvbnNpZGVyIHVwZ3JhZGluZy4gIFRoZSBtaW5vciAiZG90IHZlcnNpb24iIGlzIG5vdCBhcyBpbXBvcnRhbnQuCgojIyMjIFVwZ3JhZGluZyBmcm9tIGEgUHJldmlvdXMgVmVyc2lvbgoKYFJgIHN0b3JlcyBhbGwgdGhlIGxpYnJhcmllcyBpbiBhIHZlcnNpb24tc3BlY2lmaWMgZm9sZGVyIHN0cnVjdHVyZS4gICpCZWZvcmUqIHlvdSB1cGdyYWRlLCB5b3UgbWF5IHdhbnQgdG8gbWFrZSBhIGJhY2t1cCBvZiB3aGljaCBzZXRzIG9mIGxpYnJhcmllcy9wYWNrYWdlcyB5b3UgaGF2ZSBpbnN0YWxsZWQgYWxyZWFkeSBzbyB0aGF0IHdoZW4geW91IHVwZ3JhZGUsIHlvdSBjYW4gYXV0b21hdGljYWxseSByZWluc3RhbGwgdGhlIHBhY2thZ2VzIGludG8gdGhlIG5ldyBzeXN0ZW0uICBIZXJlIGlzIGhvdyB5b3UnZCBkbyB0aGF0LgoKMS4gTWFrZSBhIHJlY29yZCBvZiB0aGUgcGFja2FnZXMgeW91IGFscmVhZHkgaGF2ZSBpbnN0YWxsZWQuICBUaGlzIHRha2VzIHRoZSBsaXN0IG9mIHBhY2thZ2VzIHlvdSBoYXZlLCBwdWxscyBvdXQgdGhlIG5hbWVzIG9mIGVhY2ggcGFja2FnZSBhbmQgc2F2ZXMgaXQgYXMgYW4gUiBkYXRhIGZpbGUgKGAucmRhYCkgb24geW91ciBEZXNrdG9wLiAgQWZ0ZXIgdGhpcywgeW91IGNhbiBjbG9zZSBSLgoKYGBge3J9CiN8IGV2YWw6IGZhbHNlCnBrZ3MgPC0gaW5zdGFsbGVkLnBhY2thZ2VzKCkKcGtncyA8LSBuYW1lcyggaXMubmEocGtnc1ssNF0pKQpzYXZlKHBrZ3MsZmlsZT0nfi9EZXNrdG9wL3BrZ3MucmRhJykKYGBgCgoyLiBEb3dubG9hZCBhbmQgSW5zdGFsbCB0aGUgbmV3IHZlcnNpb24gb2YgYFJgLiAgCgo6Ojp7LmNhbGxvdXQtd2FybmluZ30KSWYgeW91IGFyZSBvbiBXaW5kb3dzLi4uIEknbSBzb3JyeSwgSSBmZWVsIGZvciB5b3UuICBNYWtlIHN1cmUgeW91IGluc3RhbGwgdGhlIGBVQ1JUYCBwYWNrYWdlIHRoYXQgaXMgaW4gdGhlIGBiYXNlYCBpbnN0YWxsIGRvY3VtZW50cy4gIFRoZXJlIG1heSBiZSBvdGhlciBpc3N1ZXMgdGhhdCBjb21lIHVwLCBJIGFtIG5vdCBmYW1pbGlhciB3aXRoIHRoaXMgcGxhdGZvcm0gYnV0IGluIGFsbW9zdCBhbGwgdGhlIHdvcmtzaG9wcyBJIGxlYWQsIHRoZXJlIGFyZSBXaW5kb3dzIGlzc3Vlcy4KOjo6CgozLiBGaW5kIHRoZSBwYWNrYWdlcyB5b3UgbmVlZCB0byBpbnN0YWxsIGJ5IGNvbXBhcmluZyBpdCB0byB3aGF0IHlvdSBoYWQgcHJldmlvdXNseSBhbmQgaW5zdGFsbCB0aGVtIGFsbCBhdCBvbmNlLgoKCmBgYHtyfQojfCBldmFsOiBmYWxzZQpuZXdfcGtncyA8LSBpbnN0YWxsZWQucGFja2FnZXMoKQpuZXdfcGtncyA8LSBuYW1lcyggaXMubmEobmV3X3BrZ3NbLDRdKSkKbG9hZCgifi9EZXNrdG9wL3BrZ3MucmRhIikKdG9faW5zdGFsbCA8LSBzZXRkaWZmKCBwa2dzLCBuZXdfcGtncyApCmluc3RhbGwucGFja2FnZXMoIHRvX2luc3RhbGwgKQp1cGRhdGUucGFja2FnZXMoIGFzaz1GQUxTRSApCmBgYAoKCiMjIyMgVXBkYXRpbmcgeW91ciBjdXJyZW50IHNldCBvZiBwYWNrYWdlcy4KCklmIHlvdSBqdXN0IHVwZGF0ZWQgYFJgLCB5b3UgY2FuIHNraXAgdGhpcyBzZWN0aW9uLiAgSWYgeW91IGhhdmUgbm90IHVwZGF0ZWQgeW91ciBwYWNrYWdlIGxpc3QgaW4gYSB3aGlsZSwgeW91IG1heSB3YW50IHRvIHVwZGF0ZSBhbnkgb3V0LW9mLWRhdGUgcGFja2FnZXMgeW91IG1heSBoYXZlIG9uIHlvdXIgc3lzdGVtLiAgRG8gdGhpcyB3aGVuIHlvdSBhcmUgbm90IG5lZWRpbmcgdG8gdXNlIGBSYCBmb3IgYSB3aGlsZSwgZGVwZW5kaW5nIHVwb24gaG93IG11Y2ggeW91IG5lZWQgdG8gdXBncmFkZSwgaXQgbWF5IHRha2UgYSB3aGlsZS4KCmBgYHtyfQojfCBldmFsOiBmYWxzZQp1cGRhdGUucGFja2FnZXMoIGFzaz1GQUxTRSApCmBgYAoKSWYgeW91IGRvIG5vdCBwdXQgdGhlIGBhc2s9RkFMU0VgIHBhcnQgaW4gdGhlcmUsIHlvdSdsbCBiZSB0ZXRoZXJlZCB0byB0aGUgY29tcHV0ZXIgYmVjYXVzZSBpdCB3aWxsIGFzayB5b3VyIHBlcm1pc3Npb24gdG8gaW5zdGFsbCBlYWNoIHBhY2thZ2Ugb25lIGF0IGEgdGltZS4uLiAgCgoKIyMgUlN0dWRpbwoKSSBhbSBhc3N1bWluZyB0aGF0IHlvdSBhcmUgdXNpbmcgW1JTdHVkaW9dKGh0dHBzOi8vcG9zaXQuY28pIGZvciB0aGlzIGNvdXJzZS4gIElmIHlvdSBwcmVmZXIgdG8gdXNlIGFub3RoZXIgcGxhdGZvcm0gc3VjaCBhcyBbSnVweXRlciBOb3RlYm9va3NdKGh0dHBzOi8vanVweXRlci5vcmcpLCBbVmlzdWFsIFN0dWRpb10oaHR0cHM6Ly9jb2RlLnZpc3VhbHN0dWRpby5jb20pLCBvciBvdGhlciBJREUncywgeW91IGFyZSBvbiB5b3VyIG93bi4gIEhvd2V2ZXIsIHRoaXMgY291cnNlIHdpbGwgcmVxdWlyZSB5b3UgdG8gcHJvZHVjZSBtYXJrZG93biBkb2N1bWVudHMgaW4gSFRNTCBvciBET0NYIGZvcm1hdC4KCklmIHlvdSBkbyBub3QgaGF2ZSB0aGUgbW9zdCByZWNlbnQgdmVyc2lvbiBvZiBSU3R1ZGlvLCBwbGVhc2UgZG93bmxvYWQgaXQgZnJvbSBbaGVyZV0oaHR0cHM6Ly9wb3NpdC5jbykKCgojIyBRdWFydG8KCldlIGFyZSBnb2luZyB0byB1c2UgW1F1YXJ0b10oaHR0cHM6Ly9xdWFydG8ub3JnKSBpbiB0aGlzIGNvdXJzZSB0byBtYWtlIGludGVncmF0ZSBvdXIgZGF0YSBhbmFseXNpcywgZ3JhcGhpY2FsIG91dHB1dCwgcmVwb3J0IHdyaXRpbmcuICBJZiB5b3UgaGF2ZSBub3QgdXNlZCBgcXVhcnRvYCBiZWZvcmUsIHlvdSB3aWxsIG5lZWQgdG8gZG93bmxvYWQgdGhlIG1vc3QgcmVjZW50IHZlcnNpb24uICBUaGVyZSBpcyBhIGNvbXBsZXRlIGRpc2N1c3Npb24gb2YgaG93IHRvIHN0YXJ0IHdpdGggUXVhcnRvIG9uIHRoZSB3ZWJzaXRlLiAgRG93bmxvYWQgdGhlIHZlcnNpb24gZm9yIHlvdXIgY29tcHV0ZSBwbGF0Zm9ybSBhbmQgY2hvb3NlIGBSU3R1ZGlvYCAob3Igd2hhdGV2ZXIgSURFKSB5b3UgYXJlIHVzaW5nLiAgSXQgd2lsbCBzZXQgeW91IHVwLgoKIyBSU3R1ZGlvIFByb2plY3RzCgpgUlN0dWRpb2AgaGFzIHRoZSBjb25jZXB0IG9mIGEgKnByb2plY3QqIGZvciBjb2RlIGFuZCBkYXRhIG9yZ2FuaXphdGlvbi4gIEkgKmhpZ2hseSByZWNvbW1lbmQqIHRoYXQgeW91IHVzZSBhIHNpbmdsZSBwcm9qZWN0IGZvciB0aGlzIGNvdXJzZS4gIFRoaXMgd2lsbCBjby1sb2NhdGUgYWxsIHRoZSBkYXRhLCBhbGwgdGhlIGNvZGUsIGFuZCBhbGwgdGhlIG1hcmtkb3duIHRoYXQgeW91IHdpbGwgYmUgZ2l2ZW4gYW5kIHRoYXQgeW91IGNyZWF0ZSBmb3IgdGhpcyBjbGFzcy4gIEl0IHdpbGwgYWxzbyBzZWVtbGVzc2x5IGhhbmRsZSB3b3JraW5nIGRpcmVjdG9yaWVzIGFuZCBhbGwgdGhlIG90aGVyIGNyYXAgdGhhdCBiZWZ1ZGRsZXMgbWFueSBzdHVkZW50cy4gIFRvIG1ha2UgYSBuZXcgcGFja2FnZSwgb3BlbiB1cCBgUlN0dWRpb2AgYW5kIGRvIHRoZSBmb2xsb3dpbmc6CgoKMS4gRnJvbSB0aGUgYEZpbGVgIG1lbnUsIHNlbGVjdCBgTmV3IFByb2pjdGAKIVtdKG1lZGlhL05ld19Qcm9qZWN0XzEucG5nKQoKMi4gU2VsZWN0IGBOZXcgRGlyZWN0b3J5YCBmcm9tIHRoZSBwb3B1cCBkaWFsb2cuIFRoaXMgd2lsbCBjcmVhdGUgYSBuZXcgZm9sZGVyIHRvIHB1dCBhbGwgeW91ciBtYXRlcmlhbHMgaW50by4KCiFbXShtZWRpYS9OZXdfUHJvamVjdF8yLnBuZykKCgozLiBTZWxlY3QgYE5ldyBQcm9qZWN0YCBmb3IgdGhpcywgd2UgYXJlIGdvaW5nIHRvIGNyZWF0ZSBhIGdlbmVyaWMgcHJvamVjdCB0byBwdXQgYWxsIHRoZSBjb3Vyc2UgbWF0ZXJpYWxzIGludG8uCgohW10obWVkaWEvTmV3X1Byb2plY3RfMy5wbmcpCgoKNC4gQ3JlYXRlIGEgbmFtZSBmb3IgdGhlIGZvbGRlciwgcGVyaGFwcyBzZWxlY3QgdGhlIG5hbWUgb2YgdGhlIGNsYXNzIG9yIHNvbWUgb3RoZXIgZGVzaWduYXRpb24gdGhhdCB3b3JrcyB3aXRoIHlvdXIgZmlsZSBvcmdhbml6YXRpb24gc3lzdGVtLiAgQWxzbywgcHV0IGl0IHNvbWV3aGVyZSB0aGF0IHdpbGwgYmUgdXNlYWJsZSAoSSBoYWQgYSBzdHVkZW50IHJlY2VudGx5IHdobyBvbmx5IHB1dCBzdHVmZiBpbiB0aGUgYERvd25sb2Fkc2AgZm9sZGVyIGZvciBzb21lIHJlYXNvbi4uLikuICAKCiFbXShtZWRpYS9OZXdfUHJvamVjdF80LnBuZykKCjUuIFNlbGVjdCBgQ3JlYXRlIFByb2plY3RgLgoKTm93LCB3aGVuZXZlciB5b3UgZ2V0IGEgZG9jdW1lbnQgb3IgZGF0YSBmaWxlLCAqcHV0IGl0IGluIHRoaXMgZm9sZGVyKi4gIAoKOjo6ey5jYWxsb3V0LXdhcm5pbmd9Ckl0IGlzIGltcG9ydGFudCBmb3IgbWUgdG8gZW1waGFzaXplIHRoaXMuICBXaGVuIHlvdSBkb3dubG9hZCBhIGRhdGEgc2V0IG9yIG1hcmtkb3duIGRvY3VtZW50IG9yIFIgc2NyaXB0IGFuZCBzaW1wbHkgZG91YmxlIGNsaWNrIG9uIGl0LCBpdCB3aWxsIG1vc3QgbGlrZWx5IGJlIGluIHlvdXIgRG93bmxvYWRzIGZvbGRlci4gIAoKKipNb3ZlIGl0IHRvIHRoZSBwcm9qZWN0IGZvbGRlcioqIHNvIGl0IGlzIGNvLWxvY2F0ZWQgd2l0aCBhbGwgdGhlIG90aGVyIHN0dWZmIGZvciB0aGlzIGNvdXJzZS4gIElmIHlvdSBkbyBub3QsIHNvbWUgb2YgdGhlIGNvZGUgbWF5IG5vdCB3b3JrIHByb3Blcmx5IGFuZCB5b3UnbGwgYmUgaGF0aW5nIHlvdXJzZWxmLiAgKER5ZXIncyAjMSBydWxlLCBkbyB0aGluZ3Mgbm93IHNvICdmdXR1cmUgc2VsZicgZG9lcyBub3QgaGF0ZSBkZWNpc2lvbnMgdGhhdCAncGFzdCBzZWxmJyBtYWRlLi4uKQo6OjoKCk1vcmUgaW1wb3J0YW50bHksIHdoZW4gYFJTdHVkaW9gIG9wZW5zLCBpdCB3aWxsIGRlZmF1bHQgdG8gb3BlbmluZyB0aGUgbW9zdCByZWNlbnQgcHJvamVjdCAod2hpY2ggaW4gYWxsIGxpa2VsaWhvb2Qgd2lsbCBiZSB0aGlzIHByb2plY3QpLiAgCgoKIyBNYXJrZG93bgoKSW4gYFJgLCB3ZSBoYXZlIHR3byBjYXRlZ29yaWVzIG9mIGRvY3VtZW50cyB0aGF0IHdlIHdpbGwgYmUgdXNpbmcuICAKCiMjIGBSYCBTY3JpcHRzCgoKWW91IGNhbiB3cml0ZSBwdXJlIGBSYCBjb2RlIGluIHNjcmlwdHMsIHdoaWNoIGFyZSBmaWxlcyB0aGF0IGFyZSBleGVjdXRlZCBvbmUgbGluZSBhdCBhIHRpbWUsIGlkZW50aWNhbCB0byB5b3UgdHlwaW5nIGl0IGludG8gdGhlIGBSYCBjb25zb2xlLiAgVGhlc2UgZmlsZXMgYXJlIHNhdmVkIHdpdGggdGhlIHN1ZmZpeCBgLlJgLiAgVGhlc2UgYXJlIGdyZWF0IGZvciBwdXJlIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCBjb2RpbmcuICAKClRvIGNyZWF0ZSBhbiBgUmAgU2NyaXB0IHNlbGVjdCBgRmlsZWAkXHRvJGBOZXcgRmlsZWAkXHRvJGBSIFNjcmlwdGAuICAKCiFbXShtZWRpYS9SX1NjcmlwdC5wbmcpCgpUbyBleGVjdXRlIGBSYCBjb2RlIGluIGEgc2NyaXB0LCB3ZSBjYW4gc2VsZWN0IGBTb3VyY2VgIG9yIGBSdW5gIGZyb20gdGhlIGJ1dHRvbiBhdCB0b3Agb2YgdGhlIGBSU3R1ZGlvYCBlZGl0b3Igd2luZG93IChqdXN0IGFib3ZlIHRoZSBzY3JpcHQgaXRzZWxmKS4gIE9yLCB5b3UgY2FuIGV4ZWN1dGUgdGhlIGNvZGUgaW4gYSBzY3JpcHQgZnJvbSBhbm90aGVyIGxvY2F0aW9uIChlLmcuLCBmcm9tIGFub3RoZXIgc2NyaXB0KSB1c2luZyB0aGUgZnVuY3Rpb24gYHNvdXJjZSgpYCBhbmQgcGFzc2luZyBpdCB0aGUgcGF0aCAocmVsYXRpdmUgdG8gdGhlIGRvY3VtZW50IHlvdSBhcmUgdXNpbmcgcmlnaHQgbm93KSB0byB0aGUgc2NyaXB0LiAgSXQgd2lsbCB0aGVuIGdyYWIgdGhlIGNvZGUgZnJvbSB0aGUgc2NyaXB0IGFuZCBleGVjdXRlIGl0IGFzIGlmIGl0IHdhcyBwYXJ0IG9mIHRoaXMgc2NyaXB0LiAgCgpgYGB7cn0KI3wgZXZhbDogZmFsc2UKc291cmNlKCJwYXRoX3RvL215X2Nvb2xfc2NyaXB0LlIiKQpgYGAKClRoaXMgaXMgdmVyeSBoZWxwZnVsLCBwYXJ0aWN1bGFybHkgaWYgeW91IHJvdXRpbmVseSBnZXQgcmF3IGRhdGEgdGhhdCBuZWVkcyBzb21lIHByZWxpbWluYXJ5IFFBL1FDLCB5b3UgY2FuIHNldCB1cCBhIHNjcmlwdCB0aGF0IHdpbGwgY2xlYW4gaXQgdXAgYW5kIGNhbGwgaXQgYXMgbmVjZXNzYXJ5LiAgCgo6Ojp7LmNhbGxvdXQtaW5mb30KSXQgaXMgaW1wb3J0YW50IHRvIHRoaW5rIGFib3V0IGNvbXBhcnRtZW5hdGFsaXphdGlvbiBvZiB5b3VyIGNvZGUuICBJZiB5b3UgZXZlciBmaW5kIHlvdXJzZWxmIHR5cGluZyBvciAod29yc2UgeWV0KSBjb3B5LWFuZC1wYXN0aW5nIGNvZGUgeW91J3ZlIHVzZWQgYmVmb3JlIGludG8gYSBuZXcgYW5hbHlzaXPigJRTVE9QLiAgT25lIG9mIG91ciBnb2FscyBpcyB0byB3cml0ZSAqYXMgZmV3IGxpbmVzIG9mIGNvZGUqIGFzIHBvc3NpYmxlLiAgRXJyb3JzIChsb2dpY2FsIGFuZCBncmFtbWF0aWNhbCkgaW5jcmVhc2UgbGluZWFybHkgd2l0aCB0aGUgbnVtYmVyIG9mIGxpbmVzIG9mIGNvZGUgeW91IHdyaXRlLiAgVGhlICpEKm9udCAqUiplcGVhdCAqWSpvdXJzZWxmICgqRFJZKikgcHJpbmNpcGxlIGlzIGtleSB0byBzdWNjZXNzIGluIGRhdGEgYW5hbHl0aWNzLiAgUmVmYWN0b3IgdGhpbmdzIGZvciByZXVzZSB3aWxsIGFsd2F5cyBiZSBiZW5lZml0aWNhbCB0byB5b3UgaW4gdGhlIGxvbmcgcnVuLgo6OjoKCk9uZSBsYXN0IHRoaW5nLiAgV2hlbiB5b3Ugd3JpdGUgY29kZSBpbiBhIHNjcmlwdCwgdXNlIGEgbG90IG9mIGNvbW1lbnRzIHRvIGV4cGxhaW4gd2hhdCB5b3UgYXJlIGRvaW5nIGluIHRoZSBjb2RlLiAgSGVyZSBpcyB0aGUgc3ludGF4IG9mIGEgc2luZ2xlIGxpbmUgY29tbWVudC4KCmBgYHtyfQojIFRoaXMgaXMgYSBjb21tZW50IGFuZCB3aWxsIG5vdCBiZSBpbnRlcnByZXRlZCBieSBSCmBgYAoKJ0Z1dHVyZSBzZWxmJyB3aWxsIHRoYW5rIHlvdSB3aGVuIHlvdSBjb21lIGJhY2sgdG8gdGhpcyBmaWxlIGFuZCB0cnkgdG8gZmlndXJlIG91dCBob3cgdG8gdXNlIGl0IGFnYWluLiAgCgoKCgojIyBRdWFydG8gRG9jdW1lbnRzCgpUaGUgbmV4dCBraW5kIG9mIGZpbGUgd2Ugd2lsbCBiZSB3b3JraW5nIHdpdGggYXJlIHF1YXJ0byBtYXJrZG93biBkb2N1bWVudHMuICBUaGVzZSBkb2N1bWVudHMgbGV0IHlvdSBtaXggdG9nZXRoZXIgdGhlIGBSYCBjb2RlIChsaWtlIHRoZSBzY3JpcHRzKSwgZ3JhcGhpY2FsIG91dHB1dCwgdGFidWxhciBvdXRwdXQsIGFuZCB0aGUgbGFyZ2VyIG5hcnJhdGl2ZXMgYXJvdW5kIGRhdGEgYW5kIGFuYWx5c2VzIHlvdSBhcmUgY3JlYXRpbmcuICBJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIGxpbmsgdGhlIG51bWVyaWNhbCB2YWx1ZXMgYXNzb2NhaXRlZCB3aXRoIGFuIGFuYWx5c2lzIHRvIGluLXRleHQgdmFsdWVzIHNvIHRoYXQgaWYgdGhlIGRhdGEgY2hhbmdlIHRoZSB0ZXh0IGNoYW5nZXMgYXMgd2VsbC4gIEZpZ3VyZSBhbmQgdGFibGUgbnVtYmVyaW5nIGFuZCBjaXRhdGlvbnMgYXJlIGFsc28gdXBkYXRlZCBhdXRvbWF0aWNhbGx5LiAgSXQgaXMgaW4geW91ciBiZXN0IGludGVyZXN0IChpZiB5b3VyIHRpbWUgaXMgaW1wb3J0YW50IHRvIHlvdSkgdG8gYmVjb21lIHByb2ZpY2llbnQgaW4gaG93IG1hcmtkb3duIGFuZCBgUmAgaW50ZXJhY3QuCgpUaGUgZG9jdW1lbnQgeW91IGFyZSByZWFkaW5nIHJpZ2h0IGhlcmUgaXMgYSBxdWFydG8gZG9jdW1lbnQuICBUaGVzZSBmaWxlcyBlbmQgaW4gdGhlIGZpbGUgc3VmZml4IGAucW1kYC4gIAoKSWYgeW91IGhhdmUgbm90IHVzZWQgbWFya2Rvd24gYmVmb3JlLCB5b3UgbmVlZCB0byBqdW1wIGluIHJpZ2h0IG5vdy4gIEhlcmUgYXJlIHNvbWUgcmVzb3VyY2VzIGZvciB5b3UgZnJvbSBhIERhdGEgTGl0ZXJhY3kgY291cnNlIEkgdGVhY2ggd2hpY2ggd2lsbCBoZWxwIHlvdSBnZXQgdXAtdG8tc3BlZWQgb24gdXNpbmcgTWFya2Rvd24gaW4gUlN0dWRpby4gIAoKLSBbU2xpZGVzXShodHRwczovL2R5ZXJsYWJ0ZWFjaGluZy5naXRodWIuaW8vTWFya2Rvd24vc2xpZGVzLmh0bWwpIGRlc2NyaWJpbmcgdGhlIHJhdGlvbmFsZSBmb3IgTWFya2Rvd24gYW5kIGhvdyB0byB1c2UgaXQgaW4gUlN0dWRpbyB3aGVyZSB5b3UgY2FuIG1peCB0ZXh0dWFsIGNvbnRlbnQgd2l0aCBkYXRhLCBjb2RlLCBhbmQgb3V0cHV0LiAgCi0gQSBsb25nZXIgW25hcnJhdGl2ZV0oaHR0cHM6Ly9keWVybGFidGVhY2hpbmcuZ2l0aHViLmlvL01hcmtkb3duL25hcnJhdGl2ZS5odG1sKSBvbiB0aGUgdG9waWMgdGhhdCBnb2VzIGludG8gbW9yZSBkZXB0aCB0aGFuIHRoZSBzbGlkZXMgdGhhdCBpbmNsdWRlcyBjb2RlIGFuZCBleGFtcGxlIG91dHB1dC4KLSBBIFtWaWRlb10oaHR0cHM6Ly95b3V0dS5iZS9mNTBIejkySXlJUSkgb24gTWFya2Rvd24gYW5kIE5vdGVib29rcyBpbiBSU3R1ZGlvIHRoYXQgSSBtYWRlIGR1cmluZyB0aGUgQ09WSUQgbG9ja2Rvd24gd2hlbiB0ZWFjaGluZyB0aGlzIGNvdXJzZS4KLSBFeHRlcm5hbCByZXNvdXJjZXMgcmVhbHRlZCB0byB1c2luZyBtYXJrZG93bjogIAogIC0gQSBiYXNpYyBbTWFya2Rvd24gQ2hlYXRzaGVldF0oaHR0cHM6Ly93d3cubWFya2Rvd25ndWlkZS5vcmcvY2hlYXQtc2hlZXQvKSAgCiAgLSBBIHNpdGUgYmFzZWQgdXBvbiBbUk1hcmtkb3duXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS8pIHdpdGggZXhhbXBsZXMuCiAgLSBUaGUgb2ZmaWNpYWwgZG9jdW1lbnRhdGlvbiBmb3IgW1F1YXJ0b10oaHR0cHM6Ly9xdWFydG8ub3JnL2RvY3MvZ3VpZGUvKSAod2hpY2ggaXMgdGhlIG1haW4gaW50ZXJmYWNlIHdlIHdpbGwgYmUgdXNpbmcpLgoKCiMgRGF0YSBNYW5pdXBsYXRpb24KCklmIHdlIGxvb2sgYXQgdGhlIGFtb3VudCBvZiB0aW1lIHdlIGFsbG9jYXRlIHRvIG91ciBkYXRhIGFuYWx5dGljcywgdGhlIHZhc3QgbWFqb3JpdHkgb2YgaXQgaXMgc3BlbnQgb24gd29ya2luZyBvbiB0aGUgZGF0YSwgbWFuaXB1bGF0aW5nIGl0LCBnZXR0aW5nIGl0IGludG8gYSBmb3JtYXQgdGhhdCBpcyB1c2FibGUsIGFuZCBhIHZlcnkgc21hbGwgZnJhY3Rpb24gb2YgaXQgaXMgc3BlbnQgb24gcnVubmluZyBhbiBhY3R1YWwgYW5hbHlzaXMuICBVc3VhbGx5LCBieSB0aGUgdGltZSB3ZSBnZXQgdG8gdGhlIGFuYWx5c2lzLCB3ZSBhcmUgZG9pbmcgYSBzaW1wbGUgZnVuY3Rpb24gY2FsbCB3aXRoIG91ciBmb3JtYXR0ZWQgZGF0YS4KCmBgYHtyfQojfCBldmFsOiBmYWxzZQphbmFseXNpcyggbXlEYXRhICkKYGBgCgpJIGFtIGhvcGluZyB0aGF0IHlvdSBhcmUgdmVyc2VkIGluIHVzaW5nIGB0aWR5dmVyc2VgIGZvciBkYXRhIG1hbmlwdWxhdGlvbi4gIFRoaXMgaXMgYSBjb25zdGVsbGF0aW9uIG9mIHBhY2thZ2VzIHRoYXQgaGVscCB5b3UgdGFrZSByYXcgZGF0YSAoZ2Vub3R5cGVzLCByYXN0ZXJzLCBzaGFwZWZpbGVzLCBjc3YgZmlsZXMsIGV0Yy4pIGFuZCBwZXJmb3JtIG9wZXJhdGlvbnMgb24gaXQuICBMb2FkaW5nIGluIGB0aWR5dmVyc2VgIGlzIGRvbmUgYnkgY2FsbGluZyB0aGUgbGlicmFyeSBkaXJlY3RseS4KCmBgYHtyfQpsaWJyYXJ5KCB0aWR5dmVyc2UgKQpgYGAKCklmIHlvdSBhcmUgZ2V0dGluZyBhICJsaWJyYXJ5IG5vdCBmb3VuZCIgZm9yIHRoaXMsIGluc3RhbGwgaXQgdXNpbmcgdGhlIGZvbGxvd2luZy4KCmBgYHtyfQojfCBldmFsOiBmYWxzZQppbnN0YWxsLnBhY2thZ2VzKCAidGlkeXZlcnNlIiApCmBgYAoKSGVyZSBpcyBhbiBleGFtcGxlIG9mIGhvdyBpdCBjYW4gYmUgdXNlZCB0byBzdW1tYXJpemUgZGF0YSBmcm9tIHRoZSBidWlsdC1pbiAqSXJpcyogZGF0YSBzZXQuCgpgYGB7cn0Kc3VtbWFyeSggaXJpcyApCmBgYAoKVGhlIGZvbGxvd2luZyBjb2RlIGRvZXMgdGhlIGZvbGxvd2luZzogIAotIG91dHB1dHMgdGhlIHJhdyBkYXRhLiAKLSBjcmVhdGVzIGEgbmV3IGNvbHVtbiBvZiBkYXRhIHdpdGggZ2VudXMgYW5kIHNwZWNpZXMuIAotIGdyb3VwcyBieSBzcGVjaWVzLiAKLSBzdW1tYXJpemVzIGJ5IHRha2luZyB0aGUgbWVhbiB2YWx1ZXMgZm9yIHBldGFsIGxlbmd0aCBhbmQgd2lkdGguIAotIHNhdmVzIGFzIGEgYGRhdGEuZnJhbWVgIHRvIGEgbG9jYWwgdmFyaWFibGUuICAKCmBgYHtyfQppcmlzIHw+CiAgbXV0YXRlKCBQbGFudCA9IHBhc3RlKCAiSXJpcyIsU3BlY2llcykpIHw+CiAgZ3JvdXBfYnkoIFBsYW50ICkgfD4KICBzdW1tYXJpemUoIExlbmd0aCA9IG1lYW4oIFBldGFsLkxlbmd0aCApLCAKICAgICAgICAgICAgIFdpZHRoID0gbWVhbiggUGV0YWwuV2lkdGggKSApIC0+IGlyaXNfc3VtbWFyeQpgYGAKCkFuZCB0aGlzIGlzIHdoYXQgdGhlIG91dHB1dCBsb29rcyBsaWtlLgoKYGBge3J9CmlyaXNfc3VtbWFyeSAKYGBgCgpJZiB5b3Ugd2VyZSB1bnN1cmUgd2hhdCB0aGUgb3V0cHV0IG9mIHRoZSBjb2RlIHdhcyBnb2luZyB0byBsb29rIGxpa2Ugb3IgZG8gbm90IHVuZGVyc3RhbmQgd2hhdCB3YXMgZG9uZSwgeW91IG1heSBuZWVkIHRvIHdvcmsgYSBiaXQgb24gZ2V0dGluZyB1cCB0byBzcGVlZCBvbiBgdGlkeXZlcnNlYC4KCkhlcmUgYXJlIHNvbWUgcmVzb3VyY2VzIG9uIHVzaW5nIGB0aWR5dmVyc2VgIGZyb20gYSBmZXcgZGlmZmVyZW50IHNvdXJjZXMuICAKCiAtIEEgW1ZpZGVvXShodHRwczovL3lvdXR1LmJlL2RVWnlPaFZHT2pvP3NpPUJ2YnpuSWV1YjdEX2NITlgpIGludHJvZHVjaW5nIGB0aWR5dmVyc2VgIGdpdmVuIGZyb20gYSBkaXN0cmlidXRlZCB3b3Jrc2hvcCBvbiBMYW5kc2NhcGUgR2VuZXRpY3MuICAKIC0gVXBkYXRlZCBbU2xpZGVzXShodHRwczovL2R5ZXJsYWJ0ZWFjaGluZy5naXRodWIuaW8vVGlkeXZlcnNlL3NsaWRlcy5odG1sIy90aXRsZS1zbGlkZSkgb2YgdGhlIGNvbnRlbnQuICBUaGVzZSBhcmUgdXBkYXRlZCBmcm9tIHRoZSBzZXQgaW4gdGhlIHZpZGVvIGFzIHRoZXkgYXJlIHRoZSBvbmVzIEkgdXNlZCBsYXN0IHNlbXNldGVyLiAgCiAtIEEgbGFyZ2VyIFtuYXJyYXRpdmVdKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9UaWR5dmVyc2UvbmFycmF0aXZlLmh0bWwpIGdvaW5nIGludG8gbW9yZSBkZXB0aCBvbiB0aGUgc3BlY2lmaWNzIG9mIHRoZSB0b3BpYy4gIAogLSBUaGUgZGF0YSBzZXQgd2Ugd2lsbCB1c2UgZm9yIHRoaXMgaXMgdGhlIFJpY2UgUml2ZXJzIENlbnRlciBkYXRhIHNldCAoZnJvbSAyMDE0KS4gIFlvdSBjYW4gc2VlIGl0IGhlcmUgYXMgaXRzIG5vcm1hbCBbR29vZ2xlIFNoZWV0c10oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMU1rMVlHSDlMcWpGN2RySkUtdGQxR19Ka2RBRE9VMGVNbHJQMDFXRkJUOHMvZWRpdD91c3A9c2hhcmluZykgb3IgYXMgYSBbQ1NWXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xTWsxWUdIOUxxakY3ZHJKRS10ZDFHX0prZEFET1UwZU1sclAwMVdGQlQ4cy9wdWI/Z2lkPTAmc2luZ2xlPXRydWUmb3V0cHV0PWNzdikgZmlsZS4gIAogLSBBIFtjaGVhdCBzaGVldF0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvcmF3L21haW4vZGF0YS10cmFuc2Zvcm1hdGlvbi5wZGYpIGdvaW5nIG92ZXIgdGhlIGJhc2ljIGFuYWx5c2lzIHZlcmIgZnVuY3Rpb25zIGZyb20gYGRwbHlyYC4gIAoKCiMgR3JhcGhpY3MKClBhcnQgb2YgdGhlIGB0aWR5dmVyc2VgIGlzIHRoZSBgZ2dwbG90MmAgbGlicmFyeS4gIFRoaXMgaXMgdGhlIHByaW1hcnkgYW5kIGRvbWluYW50IGdyYXBoaW5nIG91dHB1dCBmb3IgYFJgIGFuZCB3aWxsIGJlIHVzZWQgdGhyb3VnaG91dCB0aGlzIGNvdXJzZS4gIFlvdSBzaG91bGQgYmUgZmFtaWxpYXIgd2l0aCB0aGUgY29kZSBzZXRzIGJlbG93IChlbm91Z2ggdG8gdW5kZXJzdGFuZCB3aGF0IHRoZXkgYXJlIGRvaW5nKSBhbmQgYmUgYWJsZSB0byBwcm9kdWNlIGJhc2ljIGRhdGEgZ3JhcGhpY3MuCgpgYGB7cn0KI3wgZWNobzogZmFsc2UgCnRoZW1lX3NldCggdGhlbWVfbWluaW1hbCgpICkKCmBgYAoKCkEgYmFzaWMgc2NhdHRlciBwbG90IHdpdGggZ3JvdXBpbmdzIGFuZCBhIHRyZW5kIGxpbmUgKEBmaWctc2NhdHRlcikuCgpgYGB7cn0KI3wgbGFiZWw6IGZpZy1zY2F0dGVyCiN8IGZpZy1jYXA6ICJUaGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcGV0YWwgbGVuZ3RoIGFuZCB3aWR0aCBmb3IgdGhyZWUgaXJpcyBzcGVjaWVzIChpbmRpY2F0ZWQgYnkgYWx0ZXJuYXRpdmUgY29sb3JzKSB3aXRoIGxpbmVhciB0cmVuZGxpbmUgKGluIGdyYXkpLiIKCmlyaXMgfD4gCiAgZ2dwbG90KCBhZXMoUGV0YWwuV2lkdGgsIFBldGFsLkxlbmd0aCApICkgKyAKICBzdGF0X3Ntb290aCggZm9ybXVsYSA9IHl+eCwKICAgICAgICAgICAgICAgbWV0aG9kPSJsbSIsCiAgICAgICAgICAgICAgIHNlPUZBTFNFLCBjb2w9ImdyYXkiLCAKICAgICAgICAgICAgICAgbHdkPTAuNSwgbHR5PTIgKSArIAogIGdlb21fcG9pbnQoIGFlcyggY29sb3IgPSBTcGVjaWVzICkgKSArCiAgeGxhYigiUGV0YWwgV2lkdGggKG1tKSIpICsKICB5bGFiKCJQZXRhbCBMZW5ndGggKG1tKSIpCmBgYAoKCkEgY2F0ZWdvcmljYWwgZGF0YSBvdXRwdXQgdXNpbmcgYGJveHBsb3QoKWAgKEBmaWctYm94cGxvdCkgb3IgYHZpb2xpbigpYCAoQGZpZy12aW9saW4pIGRpc3BsYXkgdG8gY29tcGFyZSBjb250aW51b3VzIHJlc3BvbnNlcyBmcm9tIGZhY3RvcnMuCgoKYGBge3J9CiN8IGxhYmVsOiBmaWctYm94cGxvdAojfCBmaWctY2FwOiAiQSBib3hwbG90IHBsb3Qgb2Ygc2VwYWwgbGVuZ3RoIGZvciB0aHJlZSBpcmlzIHNwZWNpZXMuIgoKaXJpcyB8PiAKICBnZ3Bsb3QoIGFlcyhTcGVjaWVzLFBldGFsLkxlbmd0aCkgKSArIAogIGdlb21fYm94cGxvdChub3RjaD1UUlVFKSArICAKICB4bGFiKCJJcmlzIHNwZWNpZXMiKSArCiAgeWxhYigiUGV0YWwgTGVuZ3RoIChtbSkiKQpgYGAKCgpgYGB7cn0KI3wgbGFiZWw6IGZpZy12aW9saW4KI3wgZmlnLWNhcDogIkEgdmlvbGluIHBsb3Qgb2Ygc2VwYWwgbGVuZ3RoIG92ZXJsYWluIHdpdGggaW5kaXZpZHVhbCBtZWFzdXJlbWVudHMgZm9yIHRocmVlIGlyaXMgc3BlY2llcy4iCgppcmlzIHw+IAogIGdncGxvdCggYWVzKFNwZWNpZXMsUGV0YWwuTGVuZ3RoKSApICsgCiAgZ2VvbV92aW9saW4oKSArIAogIGdlb21faml0dGVyKCB3aWR0aCA9IDAuMDUsIGFscGhhID0gMC41KSArCiAgeGxhYigiSXJpcyBzcGVjaWVzIikgKwogIHlsYWIoIlBldGFsIExlbmd0aCAobW0pIikKYGBgCgpBIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdmFyaWFibGVzIChAZmlnLWRlbnNpdHkpIGFjcm9zcyBkaWZmZXJlbnQgZ3JvdXBzLgoKYGBge3J9CiN8IGxhYmVsOiBmaWctZGVuc2l0eQojfCBmaWctY2FwOiAiUHJvYmFiaWxpdHkgZGVuc2l0eSBvZiBwZXRhbCBsZW5ndGggZm9yIHRocmVlIGlyaXMgc3BlY2llcy4iCmlyaXMgfD4KICBnZ3Bsb3QoIGFlcyhTZXBhbC5MZW5ndGgsIGZpbGw9U3BlY2llcykgKSArIAogIGdlb21fZGVuc2l0eSggYWxwaGEgPSAwLjc1LCBjb2w9ImRhcmtncmF5IikgKwogIHhsYWIoIklyaXMgc3BlY2llcyIpICsKICB5bGFiKCJQZXRhbCBMZW5ndGggKG1tKSIpICsgCiAgeGxpbSggYygzLDkpICkKYGBgCgpJZiB0aGVzZSBtZXRob2RzIGFyZSBuZXcgdG8geW91LCBoZXJlIGFyZSBzb21lIHJlc291cmNlcyB0aGF0IHdpbGwgaGVscC4KCiAtIFNsaWRlcyBmb3IgW2NsYXNzaWMgcGxvdHRpbmddKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9HcmFwaGljcy1UaGF0LURvLU5vdC1TdWNrL3NsaWRlc19jbGFzc2ljLmh0bWwpIGFuZCBmb3IgW2dncGxvdDJdKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9HcmFwaGljcy1UaGF0LURvLU5vdC1TdWNrL3NsaWRlcy5odG1sKSBwbG90dGluZy4KIC0gVGhlIG5hcnJhdGl2ZSBmb3IgYm90aCBbY2xhc3NpYyBwbG90dGluZ10oaHR0cHM6Ly9keWVybGFidGVhY2hpbmcuZ2l0aHViLmlvL0dyYXBoaWNzLVRoYXQtRG8tTm90LVN1Y2svbmFycmF0aXZlX2NsYXNzaWMuaHRtbCkgYXMgd2VsbCBhcyBmb3IgW2dncGxvdDJdKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9HcmFwaGljcy1UaGF0LURvLU5vdC1TdWNrL25hcnJhdGl2ZS5odG1sKSBncmFwaGljcwogLSBbSW4gQ2xhc3NdKGh0dHBzOi8vZHllcmxhYnRlYWNoaW5nLmdpdGh1Yi5pby9HcmFwaGljcy1UaGF0LURvLU5vdC1TdWNrL2luLWNsYXNzLmh0bWwpIGRvY3VtZW50cy4KIC0gRXh0ZXJuYWwgcmVzb3VyY2VzOgogICAtIFtEYXRlIHRvIFZpel0oaHR0cHM6Ly93d3cuZGF0YS10by12aXouY29tKS4KICAgLSBbUiBHcmFwaCBHYWxsZXJ5XShodHRwczovL3ItZ3JhcGgtZ2FsbGVyeS5jb20vZ2dwbG90Mi1wYWNrYWdlLmh0bWwpCiAgIC0gW0dHUGxvdDIgQ2hlYXRzaGVldF0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvbWFpbi9kYXRhLXZpc3VhbGl6YXRpb24ucGRmKQoKIyBUYWJ1bGFyIE91dHB1dAoKVGhlIGxhc3QgcGFydCBvZiB0aGlzIHJldmlldyB3aWxsIGZvY3VzIG9uIHRhYnVsYXIgb3V0cHV0LiAgTWFueSB0aW1lcywgd2UgaGF2ZSBkYXRhIHdlIG5lZWQgdG8gc3VtbWFyaXplIChzZWUgYWJvdmUgdGhlIGB0aWR5dmVyc2VgIGV4YW1wbGUgZGF0YSB0aGF0IHByb2R1Y2VkIGEgYGRhdGEuZnJhbWVgIG9mIG1lYW4gcGV0YWwgc2l6ZXMpIGFuZCB3b3VsZCBsaWtlIHRvIGNyZWF0ZSBhIHRhYmxlIGluIG91ciBkb2N1bWVudCB0aGF0IHdpbGwgcHJlc2VudCB0aGUgcmVzdWx0cyB0byB0aGUgcmVhZGVyIChvciBhbiBBTk9WQSB0YWJsZSBvciB3aGF0ZXZlcikuICBUaGUgbW9zdCBjb21tb24gYXBwcm9hY2ggaW4gYFJgIGlzIHRvIHVzZSB0aGUgZm9sbG93aW5nOgoKYGBge3J9CiN8IG1lc3NhZ2VzOiBmYWxzZQpsaWJyYXJ5KCBrbml0ciApCmxpYnJhcnkoIGthYmxlRXh0cmEgKQpgYGAKCkhlcmUgaXMgdGhlIGRhdGEgZnJvbSBhYm92ZToKCmBgYHtyfQppcmlzX3N1bW1hcnkKYGBgCgpXZSBjYW4gbWFrZSB0aGlzIGludG8gYSB0YWJsZSBmb3IgcHVibGljYXRpb24gb3V0cHV0IGFzIGlzIChAdGJsLWlyaXMpLgoKYGBge3J9CiN8IGxhYmVsOiB0YmwtaXJpcwojfCB0YmwtY2FwOiAiTWVhbiBkaW1lbnNpb25zIGZvciBwZXRhbHMgc2FtcGxlZCBmcm9tIDUwIGluZGl2aXVkYWxzIGluIGVhY2ggaXJpcyBzcGVjaWVzLiIKaXJpc19zdW1tYXJ5IHw+CiAga2FibGUoKSB8PgogIGthYmxlX3N0eWxpbmcoIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpICkgfD4KICBhZGRfaGVhZGVyX2Fib3ZlKGMoIiAiID0gMSwgIk1lYW4gUGV0YWwgTWVhc3VyZW1lbnQgKG1tKSI9MikgKSB8PgogIGZvb3Rub3RlKGdlbmVyYWw9Ik4gPSA1MCBwZXIgc3BlY2llcy4iLCBmb290bm90ZV9hc19jaHVuayA9IFRSVUUpCiAKYGBgCgpUaGVyZSBpcyBhIGdyZWF0IHdlYnBhZ2UgZm9yIGBrYWJsZUV4dHJhYCB0aGF0IGRlc2NyaWJlcyBhIGxvdCBvZiBhZGRpdGlvbmFsIGZ1bmN0aW9uYWxpdHkgZm9yIHRoaXMgbGlicmFyeSBbaGVyZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2thYmxlRXh0cmEvdmlnbmV0dGVzL2F3ZXNvbWVfdGFibGVfaW5faHRtbC5odG1sKS4gIEkgcmVjb21tZW5kIHlvdSB0YWtlIGEgbG9vayAgYXQgaXQuCgojIENvbmNsdXNpb24KClNvIHRoaXMgc2hvdWxkIGJlIHN1ZmZpY2llbnQgZm9yIHdoYXQgd2UgbmVlZCBpbiB0aGlzIGNvdXJzZS4gIElmIHlvdSBoYXZlIGFueSBjb25jZXJucyBhYm91dCB5b3VyIGFiaWxpdHkgdG8gZG8gYW55IG9mIHRoZSBhYm92ZSwgcGxlYXNlIGxldCBtZSBrbm93IGFuZCB3ZSBjYW4gZ2V0IHRvIHdvcmsgb24gaGVscGluZyB5b3UgZ2V0IHRvIHdoZXJlIHlvdSBuZWVkIHRvIGJlLgoKSWYgeW91IGhhdmUgYW55IHF1ZXN0aW9ucywgZmVlbCBmcmVlIHRvIGRyb3AgYnkgbXkgb2ZmaWNlIChMRlNDIDEwNWMpLCBqdW1wIG9udG8gbXkgT2ZmaWNlIEhvdXJzIFpvb20gKGxpbmtlZCBmb3JtIFtDYW52YXNdKGh0dHBzOi8vY2FudmFzLnZjdS5lZCkpLCBvciBzZW5kIG1lIGFuIFtlbWFpbF0obWFpbHRvOi8vcmpkeWVyQHZjdS5lZHUpLgoKLSBEckQKCgoKCgoKCgoKCgoKCgoKCgo=